package base.thread_demo.demo19;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 当多个线程对 volatile 修饰的共享数据进行操作时,可以保证内存中该共享数据的及时可见性,
 * 相较于synchronized 是一种较为轻量级的同步策略.(这句话言外之意就是synchronized同步代码块或方法内操作的共享数据
 * 是能保证该共享数据和各个线程缓存起来的该共享数据副本之间同步的,也就是说 synchronized 是重量级实现,
 * volatile 是轻量级的实现共享数据同步的问题)
 *
 * volatile:可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,
 * 线程操作volatile 变量都是直接操作主存.
 *
 * 注意:
 * 1.volatile 不具备"互斥性"
 * 2.volatile不保证变量的"原子性"
 *
 * volatile演示时,发现有System.out.print和log.info输出都会影响案例演示的正确性,这是因为打印输出内部采用了
 * synchronized导致的,synchronized是能够保证内部数据的同步性.
 */
public class VolatileDemo01 {
    private static final Logger logger = LoggerFactory.getLogger(VolatileDemo01.class);

    public static void main(String[] args) throws InterruptedException {
        MyRunnable run = new MyRunnable();
        new Thread(run,"t1").start();

        /**
         *  main 线程这里的 Thread.sleep() 和 logger.info() 因内部加了synchronized,
         *  导致能发现和 t1 线程 共享的数据 flag
         *
         *  本案例演示 main 线程不停循环,内部检测和 t1 线程共享的数据变量 flag 来判断是否要结束循环
         */
//        Thread.sleep(1000); // 不加volatile也能检测到flag=true,内部synchronized ???
        while (true){
            if(run.isFlag()){
                logger.info("跳出循环");// 这里的logger.info()不会影响volatile效果演示,因为if判断条件始终进不来
                break;
            }
//          logger.info("---------循环-------"); // 这里不注释掉的话,即使不加volatile也能检测到flag=true,内部synchronized 导致从主存中获取flag???
        }
    }

}


class MyRunnable implements Runnable {
    private static final Logger logger = LoggerFactory.getLogger(MyRunnable.class);
    private boolean flag = false;
//    private volatile boolean flag = false;

    @Override
    public void run() {
        logger.info("change flag true ...........");
        this.flag = !this.flag;
        logger.info("flag = {}", this.flag);
    }

    // getter and setter methods
    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
